--------------------------------------------------------------------------------
-- 
-- S3PICPROG - PIC programmer with S3 starter kit
-- Copyright (C) 2008 Olivier Ringot <oringot@gmail.com>
-- 
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
-- 
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, 
-- Boston, MA  02110-1301, USA.
-- 
--------------------------------------------------------------------------------
-- 
-- $Revision: $
-- $Date: $
-- $Source: $
-- $Log: $
--
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library s3picprog_lib;
use s3picprog_lib.s3picprog_pkg.all;

entity icsp is
  port
  (
    -- system
    resetn        :  in std_logic;
    clk           :  in std_logic;
    
    -- control 
    start         :  in std_logic;
    done          : out std_logic;
    command       :  in std_logic_vector( 3 downto 0);
    datai         :  in std_logic_vector(15 downto 0);
    datao         : out std_logic_vector( 7 downto 0);
     
    -- delays
    delay_hperiod :  in std_logic_vector(15 downto 0);
    delay_prog    :  in std_logic_vector(15 downto 0);
    delay_erase   :  in std_logic_vector(15 downto 0);
    
    -- PIC pins
    pgc           : out std_logic;
    pgdo          : out std_logic;
    pgdi          :  in std_logic;
    pgde          : out std_logic
    
  );
end entity icsp;

architecture rtl of icsp is

  type t_state         is (s_idle,s_transfert);
  signal state         : t_state;
  signal delay         : std_logic_vector(18 downto 0);
  signal sr_counter    : std_logic_vector( 5 downto 0);
  signal sri           : std_logic_vector(19 downto 0);
  signal sro           : std_logic_vector(19 downto 0);

begin

  -- ---------------------------------------------------------------------------
  -- ICSP fsm
  -- ---------------------------------------------------------------------------
  p_fsm:process(clk,resetn)
  begin

    if resetn='0' then
    
      datao       <= (others=>'0');
      --
      pgc         <= '0';
      pgdo        <= '0';
      pgde        <= '1';
      done        <= '0';
      --
      sri         <= (others=>'0');
      sro         <= (others=>'0');
      sr_counter  <= (others=>'0');
      delay       <= (others=>'0');
      --
      state            <= s_idle;
    
    elsif clk'event and clk='1' then
    
      done <= '0';
      
      case state is
       
        when s_idle=>
      
          if start='1' then
          
            sr_counter <= "100111"; -- 39
            pgde       <= '1';
            pgdo       <= command(0);
            
            if command="1000" or
               command="1001" or
               command="1010" or
               command="1011" then
               
              sro        <= "00000000000000000"&command(3 downto 1);
            
            else
            
              sro        <= '0'&datai&command(3 downto 1);
            
            end if;
            
            pgc        <= '1';
            delay      <= delay_hperiod&"000";
            state      <= s_transfert;
          
          end if;
          
        when s_transfert=>
        
          if delay="0000000000000000001" then
          
            if sr_counter="010001" and 
               (command="1000" or
                command="1001" or
                command="1010" or
                command="1011") then
            
               pgde <= '0';
            
            end if;
            
            if sr_counter="000000" then
            
               if command="1000" or
                  command="1001" or
                  command="1010" or
                  command="1011" then
               
                 datao <= sri(19 downto 12);
               
               end if;
            
              done  <= '1';
              state <= s_idle;
              
            else
            
              case sr_counter is 
              
                when "100010"=> delay <= delay_prog&"000";  
                when "100001"=> delay <= delay_erase&"000";
                when others  => delay <= delay_hperiod&"000";
                
              end case;
              
              sr_counter <= sr_counter - 1;
            
            end if;
            
            if sr_counter(0)='0' then
            
              if sr_counter="000000" then
              
                pgc   <= '0';
                pgdo  <= '0';
              
              else
              
                pgc  <= '1';
                pgdo <= sro(0);
                sro <= '0'&sro(19 downto 1);
                
              end if;
            
            else
            
              sri <= pgdi&sri(19 downto 1);
              pgc <= '0';
            
            end if;
            
          else
          
            delay <= delay - 1;
          
          end if;
          
       end case;
    
    end if;
    
  end process p_fsm;

end architecture rtl;
